//#######################################################################################################
//##################################### Plugin-253 Ping #################################################
//#######################################################################################################

/*********************************************************************************************\
 * This protocol provides support for ICMP check to other (local) networked devices
 * 
 * Author             : Martinus van den Broek
 * Support            : None!
 * Date               : Jul 2014
 * Versie             : 0.2
 * Compatibility      : R744
 * Syntax             : "Ping <last IP octet>,<result var>"
 * Sample             : If your network is 192.168.0.x and the target host is 192.168.0.40, then use:
 *                         Ping 40,10
 *                      this will set Nodo variable 10 to '1' if the target is alive
 *********************************************************************************************
 * Technical data:
 * This plugin sends a simple single ICMP request with 64 bytes of null data
 * If the target host replies in any way, chosen result var will be set to '1'
 * ICMP is implemented here in a bare minimum approach, just to save code...
 \*********************************************************************************************/

#define PLUGIN_ID 253
#define PLUGIN_NAME "Ping"

#define PING_TIMEOUT 1000

#include <utility/w5100.h>

SOCKET _icmpsocket;

boolean Plugin_253(byte function, struct NodoEventStruct *event, char *string)
{
  boolean success=false;

  switch(function)
  {
#ifdef PLUGIN_253_CORE
  case PLUGIN_COMMAND:
    {
      if (!TerminalClient.connected())  // Ping only if telnet is not connected or we might run short on W5100 sockets!
        {
          Serial.println("Pinging...");

          UserVar[event->Par2 - 1] = 0;

          uint8_t IPaddress[4];
          IPaddress[0] = EthernetNodo.localIP()[0];
          IPaddress[1] = EthernetNodo.localIP()[1];
          IPaddress[2] = EthernetNodo.localIP()[2];
          IPaddress[3] = event->Par1;

          // init
          W5100.execCmdSn(_icmpsocket, Sock_CLOSE);
          W5100.writeSnIR(_icmpsocket, 0xFF);
          W5100.writeSnMR(_icmpsocket, SnMR::IPRAW);
          W5100.writeSnPROTO(_icmpsocket, IPPROTO::ICMP);
          W5100.writeSnPORT(_icmpsocket, 0);
          W5100.execCmdSn(_icmpsocket, Sock_OPEN);
          W5100.writeSnDIPR(_icmpsocket, IPaddress);
          W5100.writeSnTTL(_icmpsocket, 42);
          W5100.writeSnDPORT(_icmpsocket, 0);

          // prepare packet
          uint8_t packet[76];
          for (byte x=0; x < 76; x++) packet[x]=0;

          packet[0]=8;   // ICMP Request
          packet[2]=247; // (sum >> 8) & 0xff
          packet[3]=255; // sum & 0xff

          W5100.send_data_processing(_icmpsocket, packet, 76);
          W5100.execCmdSn(_icmpsocket, Sock_SEND);
          W5100.writeSnIR(_icmpsocket, SnIR::SEND_OK);

          // wait for reply
          Serial.println("waiting...");
          unsigned long start = millis();
          byte octet_matched = 0;

          while (millis() - start < PING_TIMEOUT)
          {
            if (W5100.getRXReceivedSize(_icmpsocket))
              {
                  uint8_t ipHeader [6];
                  uint8_t buffer = W5100.readSnRX_RD(_icmpsocket);
                  W5100.read_data(_icmpsocket, (uint8_t *)buffer, ipHeader, sizeof(ipHeader));

                  // check peer IP, must be same as requested
                  for (int x=0; x<4; x++)
                    if (IPaddress[x] == ipHeader[x])
                      octet_matched++;

                  break;
              }
          }
 
          if (octet_matched  == 4)
            {
              Serial.println("OK");
              UserVar[event->Par2 - 1] = 1;
            }
          else 
            Serial.println("Not OK");
  
          // close socket
          W5100.execCmdSn(_icmpsocket, Sock_CLOSE);
          W5100.writeSnIR(_icmpsocket, 0xFF);
        } // endif terminalsession active

      success = true;
      break;
    }
#endif // PLUGIN_253_CORE

#if NODO_MEGA
  case PLUGIN_MMI_IN:
    {
    char* str=(char*)malloc(40);
    string[25]=0;
  
    if(GetArgv(string,str,1))
      {
      if(strcasecmp(str,PLUGIN_NAME)==0)
        {
        if(GetArgv(string,str,2))
          {
          event->Par1=str2int(str);    
          if(GetArgv(string,str,3))
            {
              event->Par2=str2int(str);
              event->Type = NODO_TYPE_PLUGIN_COMMAND;
              event->Command = PLUGIN_ID;  
              success=true;
            }
          }
        }
      }
    free(str);
    break;
    }

  case PLUGIN_MMI_OUT:
    {
    strcpy(string,PLUGIN_NAME);
    strcat(string," ");
    strcat(string,int2str(event->Par1));
    strcat(string,",");
    strcat(string,int2str(event->Par2));
    break;
    }
#endif //NODO_MEGA
  }      
  return success;
}
